use ndarray::{Array2, ArrayView2};
use serde::{Deserialize, Serialize};
use std::{
    collections::{BTreeSet, HashMap},
    error::Error,
    fmt::{self, Display},
};

/// 玩家类型
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[repr(i8)]
pub enum Player {
    Black = -1,
    White = 1,
}

impl Player {
    pub fn opponent(self) -> Self {
        match self {
            Player::Black => Player::White,
            Player::White => Player::Black,
        }
    }

    pub fn switch(self) -> Self {
        self.opponent()
    }
}

impl Display for Player {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Player::Black => f.write_str("Black"),
            Player::White => f.write_str("White"),
        }
    }
}

/// 棋盘状态类
/// WIN_COND: 胜利条件连子数（五子棋则为5）
#[derive(Debug, Clone)]
pub struct BoardState<const WIN_COND: usize = 5> {
    /// 棋盘状态 (行, 列)
    board: Array2<Option<Player>>,
    /// 当前轮到哪个玩家
    current_player: Player,
    /// 各玩家连子计数: [连子数；（计数-1为下标）]
    streaks: HashMap<Player, [usize; WIN_COND]>,
    /// 所有可落子的位置 (行, 列)
    valid_moves: BTreeSet<(usize, usize)>,
}

impl<const WIN_COND: usize> BoardState<WIN_COND> {
    /// 创建空棋盘
    ///
    /// # 参数
    /// - `rows`: 棋盘行数
    /// - `cols`: 棋盘列数
    ///
    /// # 返回
    /// 初始化后的棋盘状态
    pub fn new(rows: usize, cols: usize) -> Self {
        // 创建空棋盘
        let board = Array2::from_elem((rows, cols), None);

        // 创建初始状态
        let mut state = Self {
            board,
            current_player: Player::Black, // 黑棋先行
            streaks: {
                let mut map = HashMap::new();
                map.insert(Player::Black, [0; WIN_COND]);
                map.insert(Player::White, [0; WIN_COND]);
                map
            },
            valid_moves: (0..rows)
                .flat_map(|row| (0..cols).map(move |col| (row, col)))
                .collect(),
        };

        // 初始化连子计数
        state.update_streaks();
        state
    }

    /// 从现有数组创建棋盘状态
    ///
    /// # 参数
    /// - `board`: 棋盘状态数组
    /// - `current_player`: 当前玩家
    ///
    /// # 返回
    /// 初始化后的棋盘状态
    pub fn from_array(board: Array2<Option<Player>>, current_player: Option<Player>) -> Self {
        // 收集所有可落子位置
        let valid_moves = board
            .indexed_iter()
            .filter(|(_, cell)| cell.is_none())
            .map(|(pos, _)| pos)
            .collect();
        let black_count = board
            .iter()
            .filter(|player| matches!(player, Some(Player::Black)))
            .count();
        let white_count = board
            .iter()
            .filter(|player| matches!(player, Some(Player::White)))
            .count();
        let current_player_cmp = if black_count <= white_count {
            Player::Black
        } else {
            Player::White
        };

        // 创建状态
        let mut state = Self {
            board,
            current_player: current_player.unwrap_or(current_player_cmp),
            streaks: {
                let mut map = HashMap::new();
                map.insert(Player::Black, [0; WIN_COND]);
                map.insert(Player::White, [0; WIN_COND]);
                map
            },
            valid_moves,
        };

        // 更新连子计数
        state.update_streaks();
        state
    }

    /// 解析棋盘状态字符串
    /// 格式: "B,W,.;.,B,.;W,.,B" 表示3x3棋盘
    pub fn from_status_str(
        s: &str,
        rows: usize,
        cols: usize,
        current_player: Option<Player>,
    ) -> Result<Self, Box<dyn Error>> {
        let mut board = <Array2<Option<Player>>>::from(Array2::from_elem((rows, cols), None));

        let rows_str: Vec<&str> = s.split(';').collect();
        if rows_str.len() != rows {
            return Err(format!(
                "状态字符串行数错误，应为{}行，实际{}行",
                rows,
                rows_str.len()
            )
            .into());
        }

        for (i, row_str) in rows_str.iter().enumerate() {
            let cells: Vec<&str> = row_str.split(',').collect();
            if cells.len() != cols {
                return Err(
                    format!("第{}行列数错误，应为{}列，实际{}列", i, cols, cells.len()).into(),
                );
            }

            for (j, cell) in cells.iter().enumerate() {
                board[(i, j)] = match *cell {
                    "B" => Some(Player::Black),
                    "W" => Some(Player::White),
                    "." | "" => None,
                    _ => return Err(format!("无效棋子标识: '{}'", cell).into()),
                };
            }
        }

        Ok(Self::from_array(board, current_player))
    }

    /// 初始化或重置连子计数 (完整优化版)
    /// 时间复杂度: O(rows * cols)
    pub fn update_streaks(&mut self) {
        // 重置连子计数
        self.streaks = HashMap::from([
            (Player::Black, [0; WIN_COND]),
            (Player::White, [0; WIN_COND]),
        ]);

        let (rows, cols) = self.board.dim();
        let mut streak_membership: Array2<u8> = Array2::<u8>::zeros((rows, cols)); // 标记棋子所属的连子类型
        let dirs = [(0, 1), (1, 0), (1, 1), (1, -1)]; // 四个方向

        // 孤立棋子标记
        for (row, col) in (0..rows).flat_map(|row| (0..cols).map(move |col| (row, col))) {
            // 检查是否是孤立棋子（临时标记）
            if streak_membership[(row, col)] == 0 {
                streak_membership[(row, col)] = 1;
            }
        }

        // 第一阶段：检测所有长度>=2的连子
        for (row, col) in (0..rows).flat_map(|row| (0..cols).map(move |col| (row, col))) {
            let Some(player) = self.board[(row, col)] else {
                continue;
            };

            // 检查四个方向
            for &(dr, dc) in dirs.iter() {
                // 检查是否是线段起点：反方向无同色棋子
                let prev_row = row as isize - dr;
                let prev_col = col as isize - dc;

                if self.in_bounds(prev_row, prev_col) {
                    let pr = prev_row as usize;
                    let pc = prev_col as usize;
                    if self.board[(pr, pc)] == Some(player) {
                        continue; // 不是起点
                    }
                }

                // 沿正方向计算连续长度
                let mut length = 1;
                let (mut r, mut c) = (row, col);
                let mut positions = vec![(r, c)];

                // 沿正方向延伸
                for _ in 1..WIN_COND {
                    r = r.wrapping_add_signed(dr);
                    c = c.wrapping_add_signed(dc);

                    if r >= rows || c >= cols {
                        break;
                    }

                    if self.board[(r, c)] == Some(player) {
                        length += 1;
                        positions.push((r, c));
                    } else {
                        break;
                    }
                }

                // 只处理长度>=2的连子
                if length >= 2 {
                    let record_length = length.min(WIN_COND);
                    if let Some(counts) = self.streaks.get_mut(&player) {
                        counts[record_length - 1] += 1;
                    }

                    // 标记这些棋子属于长度>=2的连子
                    for &(r, c) in &positions {
                        streak_membership[(r, c)] = 2; // 标记为非孤立棋子
                    }
                }
            }
        }

        // 第二阶段：统计孤立棋子（1连）
        for (row, col) in (0..rows).flat_map(|row| (0..cols).map(move |col| (row, col))) {
            if let Some(player) = self.board[(row, col)] {
                // 只统计标记为1的棋子（孤立棋子）
                if streak_membership[(row, col)] == 1 {
                    if let Some(counts) = self.streaks.get_mut(&player) {
                        counts[0] += 1; // 增加1连计数
                    }
                }
            }
        }
    }

    /// 落子操作
    ///
    /// # 参数
    /// - `row`: 行坐标
    /// - `col`: 列坐标
    ///
    /// # 返回
    /// 操作结果 (成功或错误信息)
    pub fn make_move(&mut self, row: usize, col: usize) -> Result<(), &'static str> {
        // 检查位置有效性
        if row >= self.board.nrows() || col >= self.board.ncols() {
            return Err("Position out of bounds");
        }

        // 检查位置是否已被占用
        if self.board[(row, col)].is_some() {
            return Err("Position already occupied");
        }

        // 落子
        self.board[(row, col)] = Some(self.current_player);

        // 更新缓存信息
        self.update_after_move(row, col);

        // 切换玩家
        self.current_player = self.current_player.switch();

        Ok(())
    }

    /// 落子后更新缓存信息 (高效实现)
    ///
    /// # 参数
    /// - `row`: 落子的行
    /// - `col`: 落子的列
    fn update_after_move(&mut self, row: usize, col: usize) {
        // 1. 更新可落子位置
        assert!(self.valid_moves.remove(&(row, col)));

        // 2. 更新连子计数
        self.update_streaks_after_move(row, col);
    }

    /// 在指定位置落子后更新连子数统计（最终版）
    /// * `row`: 落子的行坐标
    /// * `col`: 落子的列坐标
    pub fn update_streaks_after_move(&mut self, row: usize, col: usize) {
        let player = self.current_player;
        let dirs = [(0, 1), (1, 0), (1, 1), (1, -1)]; // 四个方向：水平、垂直、主对角线、副对角线

        // 辅助函数：计算指定方向上的连续棋子长度
        fn calculate_lengths(
            board: &Array2<Option<Player>>,
            row: usize,
            col: usize,
            player: Player,
            dr: isize,
            dc: isize,
        ) -> (usize, usize) {
            let (rows, cols) = board.dim();
            let in_bounds =
                |r: isize, c: isize| r >= 0 && c >= 0 && r < rows as isize && c < cols as isize;

            // 计算正方向长度
            let mut count_pos = 0;
            let (mut r, mut c) = (row as isize + dr, col as isize + dc);
            while in_bounds(r, c) && board[((r as usize), (c as usize))] == Some(player) {
                count_pos += 1;
                r += dr;
                c += dc;
            }

            // 计算反方向长度
            let mut count_neg = 0;
            let (mut r, mut c) = (row as isize - dr, col as isize - dc);
            while in_bounds(r, c) && board[((r as usize), (c as usize))] == Some(player) {
                count_neg += 1;
                r -= dr;
                c -= dc;
            }
            (count_pos, count_neg)
        }

        // 1. 移除受影响方向的原有连子计数
        for &(dr, dc) in &dirs {
            let (count_pos, count_neg) = calculate_lengths(&self.board, row, col, player, dr, dc);

            if let Some(counts) = self.streaks.get_mut(&player) {
                // 移除正方向短连
                if count_pos > 0 {
                    let idx = count_pos.min(WIN_COND) - 1;
                    counts[idx] = counts[idx].saturating_sub(1);
                }
                // 移除反方向短连
                if count_neg > 0 {
                    let idx = count_neg.min(WIN_COND) - 1;
                    counts[idx] = counts[idx].saturating_sub(1);
                }
            }
        }

        // 2. 添加新形成的连子计数
        for &(dr, dc) in &dirs {
            let (count_pos, count_neg) = calculate_lengths(&self.board, row, col, player, dr, dc);
            let total_length = count_pos + count_neg + 1;

            // 只添加最长连子计数（长度大于1）
            if total_length > 1 {
                if let Some(counts) = self.streaks.get_mut(&player) {
                    let idx = total_length.min(WIN_COND) - 1;
                    counts[idx] += 1;
                }
            }
        }

        // 3. 更新孤立棋子（1连）计数
        let mut has_neighbor = false;

        // 检查四个方向是否有相邻同色棋子
        for &(dr, dc) in &dirs {
            for sign in [-1, 1] {
                let r = row as isize + dr * sign;
                let c = col as isize + dc * sign;
                if self.in_bounds(r, c) && self.board[((r as usize), (c as usize))] == Some(player)
                {
                    has_neighbor = true;
                    break;
                }
            }
            if has_neighbor {
                break;
            }
        }

        // 更新1连计数
        let Some(counts) = self.streaks.get_mut(&player) else {
            return;
        };
        if has_neighbor {
            counts[0] = counts[0].saturating_sub(1);
        } else {
            counts[0] += 1;
        }
    }

    /// 检查坐标是否在棋盘范围内
    /// * `r`: 行坐标（isize类型）
    /// * `c`: 列坐标（isize类型）
    ///
    /// 返回布尔值表示是否在棋盘内
    fn in_bounds(&self, r: isize, c: isize) -> bool {
        let (rows, cols) = self.board.dim();
        r >= 0 && c >= 0 && r < rows as isize && c < cols as isize
    }

    /// 获取棋盘状态（不可修改）
    pub fn board_view(&self) -> ArrayView2<Option<Player>> {
        self.board.view()
    }

    /// 获取当前玩家
    pub fn current_player(&self) -> Player {
        self.current_player
    }

    /// 获取所有可落子位置
    pub fn valid_moves(&self) -> &BTreeSet<(usize, usize)> {
        &self.valid_moves
    }

    /// 获取连子计数
    pub fn get_streak(&self, player: Player, streak_count: usize) -> usize {
        assert!(1 <= streak_count && streak_count <= WIN_COND);
        self.streaks
            .get(&player)
            .map(|count_list| count_list[streak_count - 1])
            .unwrap_or(0)
    }

    pub fn check_win(&self, player: Player) -> bool {
        self.get_streak(player, WIN_COND) > 0
    }

    pub fn check_draw(&self) -> bool {
        !self.check_win(Player::Black)
            && !self.check_win(Player::White)
            && self.valid_moves.is_empty()
    }
}

/// 为棋盘状态实现Display trait，方便打印
impl<const WIN_COND: usize> fmt::Display for BoardState<WIN_COND> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (rows, cols) = (self.board.nrows(), self.board.ncols());

        // 打印列号
        write!(f, "  ")?;
        for c in 0..cols {
            write!(f, "{:2}", c)?;
        }
        writeln!(f)?;

        // 打印棋盘
        for r in 0..rows {
            write!(f, "{:2}", r)?;
            for c in 0..cols {
                let symbol = match self.board[(r, c)] {
                    Some(Player::Black) => "●",
                    Some(Player::White) => "○",
                    None => "·",
                };
                write!(f, "{:2}", symbol)?;
            }
            writeln!(f)?;
        }

        // 打印当前玩家
        writeln!(f, "Current player: {:?}", self.current_player)?;

        // 打印连子信息
        writeln!(
            f,
            "Black streaks (1-{WIN_COND}): {:?}",
            self.streaks.get(&Player::Black)
        )?;
        writeln!(
            f,
            "White streaks (1-{WIN_COND}): {:?}",
            self.streaks.get(&Player::White)
        )?;

        // 打印可落子位置数量
        writeln!(f, "Valid moves: {}", self.valid_moves.len())?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ndarray::array;

    /// 测试空棋盘初始化
    #[test]
    fn test_empty_board() {
        let board = BoardState::new(15, 15);

        // 验证棋盘尺寸
        assert_eq!(board.board.dim(), (15, 15));

        // 验证所有位置为空
        for cell in board.board.iter() {
            assert!(cell.is_none());
        }

        // 验证当前玩家（黑棋先行）
        assert_eq!(board.current_player, Player::Black);

        // 验证连子计数
        assert_eq!(board.streaks.get(&Player::Black).unwrap(), &[0, 0, 0, 0, 0]);
        assert_eq!(board.streaks.get(&Player::White).unwrap(), &[0, 0, 0, 0, 0]);

        // 验证可落子位置
        assert_eq!(board.valid_moves.len(), 15 * 15);
    }

    /// 测试从数组创建棋盘状态
    #[test]
    fn test_from_array() {
        let arr = array![
            [Some(Player::Black), None, Some(Player::White)],
            [None, Some(Player::Black), None],
            [Some(Player::White), None, Some(Player::Black)]
        ];

        let board = BoardState::<5>::from_array(arr.clone(), Some(Player::White));

        // 验证棋盘状态
        assert_eq!(board.board.dim(), (3, 3));
        assert_eq!(board.board_view(), arr);

        // 验证当前玩家
        assert_eq!(board.current_player, Player::White);

        // 验证可落子位置
        assert_eq!(board.valid_moves.len(), 4);
        assert!(board.valid_moves.contains(&(0, 1)));
        assert!(board.valid_moves.contains(&(1, 0)));
        assert!(board.valid_moves.contains(&(1, 2)));
        assert!(board.valid_moves.contains(&(2, 1)));
    }

    /// 测试落子操作
    #[test]
    fn test_make_move() {
        let mut board = BoardState::<5>::new(3, 3);

        // 第一步：黑棋落子
        assert!(board.make_move(1, 1).is_ok());
        assert_eq!(board.board[(1, 1)], Some(Player::Black));
        assert_eq!(board.current_player, Player::White);
        assert_eq!(board.valid_moves.len(), 8);

        // 第二步：白棋落子
        assert!(board.make_move(0, 0).is_ok());
        assert_eq!(board.board[(0, 0)], Some(Player::White));
        assert_eq!(board.current_player, Player::Black);
        assert_eq!(board.valid_moves.len(), 7);

        // 测试无效落子
        assert!(board.make_move(0, 0).is_err()); // 位置已被占用
        assert!(board.make_move(3, 3).is_err()); // 超出边界
    }

    /// 测试水平连子计数
    #[test]
    fn test_horizontal_streaks() {
        let mut board = BoardState::<5>::new(5, 5);

        // 黑棋水平2连
        board.make_move(0, 0).unwrap(); // ●
        board.make_move(1, 0).unwrap(); // ○
        board.make_move(0, 1).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 2), 1); // 2连计数

        // 白棋水平3连
        board.make_move(1, 1).unwrap(); // ○
        board.make_move(2, 0).unwrap(); // ●
        board.make_move(1, 2).unwrap(); // ○
        board.make_move(3, 0).unwrap(); // ●
        board.make_move(1, 3).unwrap(); // ○
        assert_eq!(board.get_streak(Player::White, 4), 1); // 4连计数

        // 黑棋水平4连
        board.make_move(0, 2).unwrap(); // ●
        board.make_move(4, 0).unwrap(); // ○
        board.make_move(0, 3).unwrap(); // ●
        board.make_move(4, 1).unwrap(); // ○
        board.make_move(0, 4).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 5), 1); // 5连计数
    }

    /// 测试垂直连子计数
    #[test]
    fn test_vertical_streaks() {
        let mut board = BoardState::<5>::new(5, 5);

        // 黑棋垂直2连
        board.make_move(0, 0).unwrap(); // ●
        board.make_move(0, 1).unwrap(); // ○
        board.make_move(1, 0).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 2), 1); // 2连计数

        // 白棋垂直3连
        board.make_move(1, 1).unwrap(); // ○
        board.make_move(2, 4).unwrap(); // ●
        board.make_move(2, 1).unwrap(); // ○
        board.make_move(0, 2).unwrap(); // ●
        board.make_move(3, 1).unwrap(); // ○
        assert_eq!(board.get_streak(Player::White, 4), 1); // 4连计数

        // 黑棋垂直4连
        board.make_move(1, 2).unwrap(); // ●
        board.make_move(4, 1).unwrap(); // ○
        board.make_move(2, 2).unwrap(); // ●
        board.make_move(3, 3).unwrap(); // ○
        board.make_move(3, 2).unwrap(); // ●
        board.make_move(4, 3).unwrap(); // ○
        board.make_move(4, 2).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 5), 1); // 5连计数(0,2) -> (4,2)
    }

    /// 测试对角线连子计数
    #[test]
    fn test_diagonal_streaks() {
        let mut board = BoardState::<5>::new(6, 6);

        // 主对角线 (左上到右下)
        board.make_move(1, 1).unwrap(); // ●
        board.make_move(0, 0).unwrap(); // ○
        board.make_move(2, 2).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 2), 1); // 2连计数

        board.make_move(1, 0).unwrap(); // ○
        board.make_move(3, 3).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 2), 0); // 2连计数
        assert_eq!(board.get_streak(Player::Black, 3), 1); // 3连计数

        // 副对角线 (右上到左下)
        board.make_move(1, 4).unwrap(); // ○
        board.make_move(2, 0).unwrap(); // ●
        board.make_move(2, 3).unwrap(); // ○
        board.make_move(3, 1).unwrap(); // ●
        board.make_move(3, 2).unwrap(); // ○
        board.make_move(4, 0).unwrap(); // ●
        assert_eq!(board.get_streak(Player::White, 3), 1); // 3连计数
    }

    /// 测试复杂连子场景
    #[test]
    fn test_complex_streak_scenarios() {
        // 设置棋盘状态
        // 0: ● ● ● ○ ·
        // 1: ○ ● ● ○ ●
        // 2: ● ○ ● ○ ○
        // 3: ○ ● ○ ● ●
        // 4: · ○ ● ○ ●
        let chess_array = array![
            [
                Some(Player::Black),
                Some(Player::Black),
                Some(Player::Black),
                Some(Player::White),
                None
            ],
            [
                Some(Player::White),
                Some(Player::Black),
                Some(Player::Black),
                Some(Player::White),
                Some(Player::Black)
            ],
            [
                Some(Player::Black),
                Some(Player::White),
                Some(Player::Black),
                Some(Player::White),
                Some(Player::White)
            ],
            [
                Some(Player::White),
                Some(Player::Black),
                Some(Player::White),
                Some(Player::Black),
                Some(Player::Black)
            ],
            [
                None,
                Some(Player::White),
                Some(Player::Black),
                Some(Player::White),
                Some(Player::Black)
            ]
        ];

        let mut board = BoardState::<5>::from_array(chess_array, None);

        // 手动更新连子计数
        board.update_streaks();

        // 验证黑棋连子
        assert_eq!(board.streaks.get(&Player::Black).unwrap(), &[1, 7, 4, 0, 1]);

        // 验证白棋连子
        assert_eq!(board.streaks.get(&Player::White).unwrap(), &[0, 4, 2, 1, 0]);
        assert_eq!(board.get_streak(Player::White, 2), 4); // 2连
        assert_eq!(board.get_streak(Player::White, 3), 2); // 3连
        assert_eq!(board.get_streak(Player::White, 4), 1); // 3连
    }

    /// 测试边界情况
    #[test]
    fn test_edge_cases() {
        // 1. 最小棋盘 (1x1)
        let mut board = BoardState::new(1, 1);
        board.make_move(0, 0).unwrap();
        assert_eq!(board.streaks.get(&Player::Black).unwrap(), &[1, 0, 0, 0, 0]); // 单个棋子没有连子
        assert_eq!(board.valid_moves.len(), 0);

        // 2. 棋盘满子
        let mut board = BoardState::<5>::new(2, 2);
        board.make_move(0, 0).unwrap();
        board.make_move(0, 1).unwrap();
        board.make_move(1, 0).unwrap();
        board.make_move(1, 1).unwrap();
        assert_eq!(board.valid_moves.len(), 0);

        // 3. 长连子 (超过6连)
        let mut board = BoardState::<6>::new(10, 2);
        for i in 0..9 {
            board.make_move(i, 0).unwrap(); // 黑棋
            board.make_move(i, 1).unwrap(); // 白棋
        }
        // 7连会被统计成6连 (只统计2-6连)
        assert_eq!(
            (
                board.streaks.get(&Player::Black).unwrap(),
                board.streaks.get(&Player::White).unwrap()
            ),
            (&[0, 0, 0, 0, 0, 1], &[0, 0, 0, 0, 0, 1])
        );
    }

    /// 测试增量更新正确性
    #[test]
    fn test_incremental_update() {
        let mut board = BoardState::new(5, 5);

        // 初始状态
        board.make_move(2, 2).unwrap();

        // 创建相同棋盘的完整扫描版本
        let mut full_scan_board =
            BoardState::<5>::from_array(board.board.clone(), Some(board.current_player));
        full_scan_board.update_streaks();

        // 比较增量更新和完整扫描结果
        assert_eq!(board.streaks, full_scan_board.streaks);

        // 添加更多棋子
        board.make_move(2, 3).unwrap();
        board.make_move(2, 1).unwrap();

        // 再次比较
        let mut full_scan_board2 =
            BoardState::<5>::from_array(board.board.clone(), Some(board.current_player));
        full_scan_board2.update_streaks();
        assert_eq!(board.streaks, full_scan_board2.streaks);
    }

    /// 测试可落子位置更新
    #[test]
    fn test_valid_moves_update() {
        let mut board = BoardState::<5>::new(3, 3);
        let _initial_moves = board.valid_moves.clone();

        // 落子后位置应被移除
        board.make_move(1, 1).unwrap();
        assert_eq!(board.valid_moves.len(), 8);
        assert!(!board.valid_moves.contains(&(1, 1)));

        // 再次落子
        board.make_move(0, 0).unwrap();
        assert_eq!(board.valid_moves.len(), 7);
        assert!(!board.valid_moves.contains(&(0, 0)));

        // 无效落子不应改变状态
        let before = board.valid_moves.clone();
        board.make_move(1, 1).unwrap_err();
        assert_eq!(board.valid_moves, before);
    }

    /// 测试玩家切换
    #[test]
    fn test_player_switching() {
        let mut board = BoardState::<5>::new(3, 3);

        assert_eq!(board.current_player, Player::Black);
        board.make_move(0, 0).unwrap();
        assert_eq!(board.current_player, Player::White);

        board.make_move(0, 1).unwrap();
        assert_eq!(board.current_player, Player::Black);

        board.make_move(0, 2).unwrap();
        assert_eq!(board.current_player, Player::White);
    }

    /// 测试连子计数重置
    #[test]
    fn test_streak_reset() {
        let mut board = BoardState::<5>::new(3, 3);

        // 创建2连
        board.make_move(0, 0).unwrap(); // ●
        board.make_move(1, 0).unwrap(); // ○
        board.make_move(0, 1).unwrap(); // ●
        assert_eq!(board.get_streak(Player::Black, 2), 1); // 2连

        // 打断连子
        board.make_move(0, 2).unwrap(); // ○
        assert_eq!(board.get_streak(Player::White, 2), 0); // 没有2连
    }
}
